﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using Xpedite.Resources.Definitions;

/* Copyright (c) 2010 CBaxter
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
 * IN THE SOFTWARE. 
 */

namespace Xpedite.Resources.Combiners
{
  internal abstract class SpriteCombinerBase : ICombiner
  {
    public IResourceContainer Combine(IResourceUrl virtualUrl, IResourceDefinition definition)
    {
      Verify.True(
        definition.ResourceType == ResourceType.Png || definition.ResourceType == ResourceType.Gif || definition.ResourceType == ResourceType.Jpeg, 
        "definition.ResourceType", 
        "Value must be Gif, Jpeg or Png."
        );

      return new ResourceContainer(
        GetResourceType(definition),
        virtualUrl,
        () =>
        {
          using (var spriteLayout = ComputeSpriteLayout(String.Format("xpedite-{0}-{1}", definition.ResourceType.ToString().ToLowerInvariant(), definition.ReferenceName), definition.References))
          {
            return BuildResource(virtualUrl, definition, spriteLayout);
          }
        });
    }

    protected virtual ResourceType GetResourceType(IResourceDefinition definition)
    {
      return definition.ResourceType;
    }

    protected abstract IResource BuildResource(IResourceUrl virtualUrl, IResourceDefinition definition, ISpriteLayout spriteLayout);

    private static ISpriteLayout ComputeSpriteLayout(String name, IEnumerable<INamedValue<IResourceContainer>> resources)
    {
      var coordinate = new Point();
      var spriteLayout = new SpriteLayout { Name = name, Images = new List<ISpritePosition>() };
      var imageResources = resources.OrderBy(item => item.Name)
                                    .Where(item => typeof(IImageResource).IsAssignableFrom(item.Value.Resource.GetType()))
                                    .GroupBy(item => ((IImageResource)item.Value.Resource).Size.Height)
                                    .OrderBy(grouping => grouping.Key);

      foreach (var grouping in imageResources)
      {
        Int32 rowHeight = grouping.Key;

        foreach (var group in grouping)
        {
          var image = (IImageResource)group.Value.Resource;
          spriteLayout.Images.Add(new SpritePosition { Name = group.Name, Image = LoadImage(image), Position = new Rectangle(coordinate, image.Size) });
          coordinate.X += image.Size.Width;
          spriteLayout.Width = Math.Max(coordinate.X, spriteLayout.Width);
        }

        coordinate = new Point(0, coordinate.Y + rowHeight + 1);

        spriteLayout.Height = coordinate.Y;
      }

      return spriteLayout;
    }

    private static Image LoadImage(IResource resource)
    {
      using (var memoryStream = new MemoryStream(resource.Bytes, false))
        return Image.FromStream(memoryStream);
    }
  }
}
